#pragma once

#ifdef _DEBUG
#define AssertModule(ptn) if (ptn == NULL) {ViaMain::Log(__FILE__, __LINE__, __FUNCTION__, #ptn, 3, "NULL module detected!\n%s not initialized!", #ptn);}
#define AssertLogI(condition, format, ...) if (condition) {ViaMain::Log(__FILE__, __LINE__, __FUNCTION__, #condition, 0, format, __VA_ARGS__);}
#define AssertLogW(condition, format, ...) if (condition) {ViaMain::Log(__FILE__, __LINE__, __FUNCTION__, #condition, 1, format, __VA_ARGS__);}
#define AssertLogE(condition, format, ...) if (condition) {ViaMain::Log(__FILE__, __LINE__, __FUNCTION__, #condition, 2, format, __VA_ARGS__);}
#define AssertLogC(condition, format, ...) if (condition) {ViaMain::Log(__FILE__, __LINE__, __FUNCTION__, #condition, 3, format, __VA_ARGS__);}

#else
#define AssertModule(ptn) 0;
#define AssertLogI(condition, format, ...) condition;
#define AssertLogW(condition, format, ...) if (condition) {ViaMain::Log(__FILE__, __LINE__, __FUNCTION__, #condition, 1, format, __VA_ARGS__);}
#define AssertLogE(condition, format, ...) if (condition) {ViaMain::Log(__FILE__, __LINE__, __FUNCTION__, #condition, 2, format, __VA_ARGS__);}
#define AssertLogC(condition, format, ...) if (condition) {ViaMain::Log(__FILE__, __LINE__, __FUNCTION__, #condition, 3, format, __VA_ARGS__);}

#endif

class IEngMod;
class IGame;
class Timer;
class ICriticalSection;

class ViaMain
{
public:
	static void ForceExit();

	static void PlayGame(IGame* game);
	static void StopGame();

	static void Log(const char* file, int line, const char* func, const char* code, int priority, const char* format, ...);
	static void Message(const char* file, int line, const char* func, const char* format, ...);
	static void Timestamp(const char* format, ...);

	static void Init(IEngMod* mod);

	template<typename T>
	static void Free(T** modPtn)
	{
		if (modPtn && *modPtn)
		{
			for (std::vector<IEngMod*>::iterator iter = m_modulesPtnVec.begin(); iter != m_modulesPtnVec.end();)
			{
				if (*iter == *modPtn)
				{
					delete *modPtn;
					*modPtn = NULL;

					iter = m_modulesPtnVec.erase(iter);
					return;
				}
				else
				{
					++iter;
				}
			}

			AssertLogC(true, "Module %s not found in modules vector.", (*modPtn)->GetModuleName());
		}
	}

	static void CallOnLoop(IEngMod* func, uint arg, uint index, const std::string& desc);
	static void StartBenchmark();

private:
	struct FunctionCall
	{
		IEngMod*		m_mod;
		uint			m_index;
		uint			m_arg;
		std::string		m_description;
	};

	ViaMain();
	~ViaMain();

	static void			Initialize();
	static void			Loop();
	static void			Finalize();

	static void			LogCallOnLoopEvents();

	static void			WriteLog(const char* file, int line, const char* func, int priority, const char* text);

	static FILE*		m_log;
	static bool			m_isLoopActive;

	static void			BenchmarkBegin();
	static void			BenchmarkEnd();
	static bool			m_benchmarkEnabled;
	static bool			m_benchmarkNextFrame;
	static Timer		m_benchmarkTimer;
	static ICriticalSection* m_benchmarkCriticalSection;

	static std::vector<IEngMod*>				m_modulesPtnVec;
	static std::vector<struct FunctionCall>		m_functionsOrder;
};
